<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        /* 
         /* 
                闭包
        1. 函数的两个阶段
          + 函数定义阶段
            => 在堆内存中开辟一段存储空间
            => 把函数体内的代码放在这个存储空间内
            => 存储空间地址赋值给 栈内存变量
          + 函数调用阶段
            => 按照栈内存变量存储的地址找到对应的堆内存存储空间
            => 在调用栈内开辟一段新的函数执行空间
            => 在执行空间内进行形参赋值
            => 在执行空间内进行预解析
            => 把函数存储空间的代码复制一份到执行空间内执行
            => 执行完毕所有代码, 本次开启的执行空间销毁
        2. 一个不会销毁的空间
          + 条件1: 函数内返回一个复杂数据类型
          + 条件2: 函数外部有变量接受该函数的返回值
        3. 闭包
          + 条件1: 一个不会销毁的空间
          + 条件2: 外部函数内 直接 或者 间接 返回一个函数
          + 条件3: 内部函数要使用着外部函数的私有变量 或者参数 
        4. 闭包的特点
          + 可以在函数外部访问函数内部的私有变量
          + 延长变量的生命周期
          + 一个不会销毁的空间

          应用场景：
            1. 使用函数的私有变量 
            2. 定时器第一个参数（函数）携带参数
            3. 沙箱模式 
            4. 循环绑定事件 获取对应元素的下标
            5. 函数柯理化 
        5. 沙箱
          + 对函数内的私有变量进行暴露, 可以在外部进行操作
            function outer() {
              let num = 100
              let str = 'hello world'

              const obj = {
                getNum () { return num },
                setNum (val) { num = val },
                getStr () { return str }
              }

              return obj
            }
            const res = outer()
            // res 接受到的就是一个对象
            // 你需要获取 outer 内 num 的值, res.getNum()
            // 你需要获取 outer 内 str 的值, res.getStr()
            // 你需要设置 outer 内 num 的值, res.setNum(300)
        6. 沙箱语法糖
          + 利用 getter 获取器 和 setter 设置器 来修饰对象内的函数
          function outer() {
              let num = 100
              let str = 'hello world'

              const obj = {
                get num () { return num },
                set num (val) { num = val },
                get str () { return str }
              }

              return obj
            }
            const res = outer()
            // res 接受到的就是一个对象
            // 你需要获取 outer 内 num 的值, res.num
            // 你需要获取 outer 内 str 的值, res.str
            // 你需要设置 outer 内 num 的值, res.num = 300

      柯理化函数
        + 把一个一次传递多个参数的函数, 修改成多次调用每次一个(或者多个) 的函数
        + 意义: 可以把一些预制内容提前保存下来, 利用的就是闭包延长变量生命周期的特点
          function fn(a) {
            return function (b) {
              return a + b
            }
          }
          const f = fn(10)
          // f 得到一个函数 没有执行 
          // 当你调用 f(20) 得到的就是 10 + 20
          // 当你调用 f(50) 得到的就是 10 + 50
        

        // 本来是一次传递四个 
        // 可以实现  分好几次传 
        // 第一次传三个 两个  1个 或者 0个都行 
        // 如果第一次传3个 第二次就传一个
        // 如果第一次传2个 第二次传一个或者两个都可以
        // 如果第一次传1个 第二次传3个或者两个 或者1都可以  
        // 如果第一次传0个 第二次传4个或者3个 或者2 或者 1个都可以  

        // 准备一个功能函数  实现拼接功能
        function fn1(num1,num2,num3,num4){
            return num1+'://'+num2+':'+num3+num4
        }

        // 柯理化函数的思路是   
        // 判断你传过去参数的个数 如果达到功能函数的要求 执行功能函数
        // 没有达到  调用自己继续收集参数直到达到要求  


        function currying(cb, ...params) {
            let maxParams = cb.length; //  获取功能函数参数的个数
            // 接受传过来的参数
            // let args = params || []
            let args = [...params]


            function inner(...p) {
              const arr = [ ...args, ...p ]

              if (arr.length < maxParams) {
                return currying(cb, ...arr); // 需要继续收集 
                // 需要把收集好的参数 (数组) 展开以后赋给currying
                //如果不展开  curry参数会是个二维数组  
              } else {
                return cb(...arr); // 执行功能函数 
              }
            }

            return inner
          }
        



          
        */
        
        
        
    </script>
</body>
</html>